#ifndef __M_UTIL_H__
#define __M_UTIL_H__
#include "logger.hpp"
#include <iostream>
#include <mysql/mysql.h>
#include <string>
#include <sstream>
#include <vector>
#include <jsoncpp/json/json.h>
#include <memory>
#include <fstream>

#include<websocketpp/server.hpp> // WebSocket++服务器头文件
#include<websocketpp/config/asio_no_tls.hpp> // WebSocket++ ASIO非TLS配置头文件

// 定义WebSocket服务器类型，使用ASIO非TLS配置
typedef websocketpp::server<websocketpp::config::asio> wsserver_t;


class mysql_util
{
public:
    //
    static MYSQL *mysql_create(const std::string &host,
                               const std::string &username,
                               const std::string &password,
                               const std::string &dbname,
                               uint16_t port = 3306)
    {
        // 1.创建数据库句柄
        // MYSQL* mysql_init(MYSQL* mysql);

        MYSQL *mysql = mysql_init(NULL);
        if (mysql == NULL)
        {
            ERR_LOG("建立失败\n");
            return NULL;
        }

        // 2.连接数据库服务器
        // MYSQL *mysql_real_connect(mysql,host,user,pass,dbname,port,unix_socket,flag)

        if (mysql_real_connect(mysql, host.c_str(), username.c_str(), password.c_str(), dbname.c_str(), port, NULL, 0) == NULL)
        {
            ERR_LOG("连接数据库失败: %s", mysql_error(mysql));
            mysql_close(mysql);
            return NULL;
        }

        // 3.设置字符集，utf8
        // int mysql_set_character_set(mysql,"utf8")

        if (mysql_set_character_set(mysql, "utf8") != 0)
        {
            ERR_LOG("设置字符集失败: %s", mysql_error(mysql));
            mysql_close(mysql);
            return NULL;
        }

        return mysql;
    }

    // 执行mysql指令
    static bool mysql_exec(MYSQL *mysql, const std::string &sql)
    {
        // 5.执行sql语句---------增删改查
        // int mysql_query(MYSQL *mysql,char* sql);
        // char* sql="insert stu values(null,'张三',18,10,10,10);";//增
        // char* sql="update stu set ch=ch+30 where sn=1;";//改
        // char* sql="delete from stu where sn=1;";//删
        // char* sql = "select * from stu;";//查询

        if (mysql_query(mysql, sql.c_str()) != 0) // 传的是string类，但参数类型是const char * ，所以要使用string.c_str()
        {
            ERR_LOG("添加内容失败: %s\n", mysql_error(mysql));
            return false;
        }
        return true;
    }

    // 销毁mysql句柄
    static void mysql_destroy(MYSQL *mysql)
    {
        if (mysql != NULL)
        {
            mysql_close(mysql);
        }

        return;
    }
};

class json_util
{
public:
    // 在外部需要把需要使用的资源已经放入val对象中！！！！！！！！----------和之前写的json.cpp中少一步
    static bool serialize(const Json::Value &root, std::string &str) // 引用别忘了要加！！！！
    {
        // 2.示例话一个StreamWriterBuilder工厂类
        Json::StreamWriterBuilder swb;
        // 3.使用这个工厂类来生产一个StreamWriter对象,这次使用的是智能指针，不需要自己再delete了
        std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
        // 4.使用StreamWriter，对val里面的数据进行序列化
        std::stringstream ss;
        int ret = sw->write(root, &ss); // 序列化
        if (ret != 0)
        {
            ERR_LOG("Serialize failed!\n");
            return false;
        }
        str = ss.str();
        return true;
    }

    static bool unserialize(const std::string &str, Json::Value &root)
    {
        // 1.示例化一个Json::CharReaderBuilder工厂类
        Json::CharReaderBuilder crb;
        // 2.通过这个工厂类来示例化一个Json::CharReader对象,这里也使用智能指针
        std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
        // 3.初始化一个Json::Value对象来存储数据,这次不需要，因为我需要把内容放入root
        std::string err;
        // 4.使用CharReader对象对JSON格式的字符串str进行反序列化
        bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), &root, &err);
        if (ret == false)
        {
            ERR_LOG("Serialize failed! : %s \n", err.c_str());
            return false;
        }
        return true;
    }
};

class string_util
{
public:
    static int split(const std::string &src, const std::string &sep, std::vector<std::string> &res)
    {
        // 123,234,,,,345
        size_t pos, idx = 0;
        while (idx < src.size())
        {
            pos = src.find(sep, idx); // find函数会返回找到的子串的起始位置，即开头的位置
            if (pos == std::string::npos)
            {
                // 没有找到,字符串中没有间隔字符了，则跳出循环
                res.push_back(src.substr(idx));
                break;
            }
            if (pos == idx)
            {
                idx += sep.size();
                continue;
            }
            res.push_back(src.substr(idx, pos - idx)); // 参数是起始位置和要截取的长度
            // idx += sep.size()这么写是错误的！！！！！！！！
            idx = pos + sep.size(); // 这才是正确的
        }
        return res.size();
    }
};

class file_util
{
public:
    static bool read(const std::string &filename, std::string &body)
    {
        // 打开文件
        std::ifstream ifs(filename, std::ios::binary); // 二进制形式打开 ifstream(name , 宏)
        if (ifs.is_open() == false)//ifstream.is_open--------判断是否能打开
        {
          //  ERR_LOG("%s file open failed", filename.c_str());
            return false;
        }
        // 获取文件大小
        ifs.seekg(0, std::ios::end);//ifstream.seekg(),----------(偏移量, 基准位置)，指的是先移动到基准位置，然后再移动偏移量！！！这次使用的意思是移动到文件末尾后不用再移动了
        size_t fsize = 0;
        fsize = ifs.tellg(); //ifstring.tellg()返回当前文件指针的位置,在文件末尾则相当于返回文件大小
        ifs.seekg(0, std::ios::beg);//这次使用的意思是移动到文件起始后不用再移动了
        body.resize(fsize);
        // 读取文件内容到body
        ifs.read(&body[0], fsize);//ifstring.read(char*)-------body[0] 返回的是字符的引用（char&），而非指针,body本身则是类无法转换为char*
        if (ifs.good() == false)
        {
            ERR_LOG("%s file read failed", filename.c_str());
            return false;
        }
        // 关闭文件
        ifs.close();//ifstring.close()关闭文件
        return true;
    }
};

#endif